#pragma once
#include "CameraPrecompiled.hxx"

namespace Eifersucht
{
	class BasicCamera
	{
#pragma region Internal shared types
	public: // Internal shared types

		enum class PointerButton
		{
			Left = BitMask(0), 
			Right = BitMask(1),
			Middle = BitMask(2),
			Wheel = BitMask(3)
		};

		enum class KeyboardButton
		{
			Left = 0, 
			Right = 1, 
			Forward = 2, 
			Backward = 3, 
			Up = 4, 
			Down = 5, 
			Reset = 6, 
			Ctrl = 7, 
			NumKeys = 8, 
			Unknown = 0xff,
		};

		enum class ButtonState
		{
			WasPressed = 0x80,
			IsPressed = 0x01,
		};

		typedef RECT Rect;						// Camera rect.
		typedef POINT Point;					// Camera point.
		typedef DirectX::XMINT2 Int32x2;		// Camera int x 2.
		typedef Reign::Vector2 Float32x2;		// Camera float x 2.
		typedef Reign::Vector3 Float32x3;		// Camera float x 3.
		typedef Reign::Vector4 Float32x4;		// Camera float x 4.
		typedef Reign::Matrix Float32x4x4;		// Camera float x 16.
		typedef Reign::Quaternion Quaternion;	// Camera float x 4.
		typedef concurrency::concurrent_unordered_map<UInt32, Byte> KeyMap; // Keyboard key mapping.
		typedef std::pair<UInt32, Byte> KeyPair; // Keyboard key pair.

		TinyTypeDecl(Rect);						// Camera rect.
		TinyTypeDecl(Point);					// Camera point.
		TinyTypeDecl(KeyMap);					// Keyboard key mapping.
		TinyTypeDecl(KeyPair);					// Keyboard key pair.
		TinyTypeDecl(Int32x2);					// Camera uint x 2.
		TinyTypeDecl(Float32x2);				// Camera float x 2.
		TinyTypeDecl(Float32x3);				// Camera float x 3.
		TinyTypeDecl(Float32x4);				// Camera float x 4.
		TinyTypeDecl(Quaternion);				// Camera float x 4.
		TinyTypeDecl(Float32x4x4);				// Camera float x 16.

#pragma endregion Internal shared types

#pragma region Properties
	public:

		inline Float32x4x4ConstPtr GetProjection() { return &m_f4x4Proj; }
		inline Float32x4x4ConstPtr GetView() { return &m_f4x4View; }
		inline Float32x3ConstPtr GetTarget() { return &m_f3LookAt; }
		inline Float32x3ConstPtr GetEye() { return &m_f3Eye; }
		inline KeyMapPtr GetKeyMap() { return &m_KeyMap; }
		inline Float32 GetNear() { return m_fNearPlane; }
		inline Float32 GetFar() { return m_fFarPlane; }
		inline HWND GetWindow() { return m_hWindow; }

		inline Bool IsBeingDragged() { return m_bPointerLeftButtonPressed || m_bPointerMiddleButtonPressed || m_bPointerRightButtonPressed; }
		inline Bool IsPointerMiddleButtonPressed() { return m_bPointerMiddleButtonPressed; } 
		inline Bool IsPointerRightButtonPressed() { return m_bPointerRightButtonPressed; } 
		inline Bool IsPointerLeftButtonPressed() { return m_bPointerLeftButtonPressed; } 

		inline Void SetEnableVerticalMovement( _In_ Bool bEnable ) { m_bEnableVerticalMovement = bEnable; } 
		inline Void SetEnablePointerCapture( _In_ Bool bEnable ) { m_bAllowPointerCapturing = bEnable; } 
		inline Void SetInvertPitch( _In_ Bool bInvertPitch ) { m_bInvertPitch = bInvertPitch; }
		inline Void SetEnableMovement( _In_ Bool bEnable ) { m_bEnableMovement = bEnable; } 
		inline Void SetDragRect( _In_ RectConstRef l4Rect ) { m_l4Drag = l4Rect; }
		inline Void SetWindow( _In_ HWND hWindow ) { m_hWindow = hWindow; }

		inline Void SetKeyMap( _In_ KeyMapPtr pMap ) 
		{  
			if ( pMap )
			{
				m_KeyMap.clear(); 
				m_KeyMap = (*pMap);
			}
		}

		inline Void BindKey( _In_ UInt32 u32Key, _In_ Byte i8Key )
		{
			m_KeyMap.insert(KeyPair(u32Key, i8Key));
		}

		inline Void UnbindKey( _In_ UInt32 u32Key )
		{
			m_KeyMap.unsafe_erase(u32Key);
		}

		inline Void BindKey( _In_ KeyPair u32i8Pair )
		{
			m_KeyMap.insert(u32i8Pair);
		}

		inline Void SetFactors( _In_ Float32 fOnMovement = 0.01f, _In_ Float32 fOnRotation = 5.0f )
		{
			m_fMovementFactor = fOnMovement;
			m_fRotationFactor = fOnRotation;
		}

		inline Void SetPointerSmoothFrames( _In_ Int32 iFrames )
		{
			if ( iFrames > 0 ) m_fPointerSmoothFrames = _To_float iFrames;
		}

		inline Void SetResetPointerAfterMovement( _In_ Bool bReset )
		{
			m_bResetPointerAfterMovement = bReset;
		}

		inline Void SetDrag( _In_ Bool bDrag, _In_ Float32 fTimeDragToZero ) 
		{ 
			m_fTotalDragTimeToZero = fTimeDragToZero; 
			m_bMovementDrag = bDrag; 
		}

		inline Void SetClipBoundary( _In_ Bool bClipToBoundary, _In_ Float32x3ConstPtr pf3Min, _In_ Float32x3ConstPtr pf3Max )
		{
			if ( pf3Min ) m_f3MinBoundary = (*pf3Min);
			if ( pf3Max ) m_f3MaxBoundary = (*pf3Max);
			m_bClipToBoundary = bClipToBoundary;
		}
		
#pragma endregion Properties

#pragma region Members
	protected:

		/// Input members

		Int32 m_iKeysPressed;
		Byte m_i8x8Keys[KeyboardButton::NumKeys]; // Start of input = WasDown | IsDown
		Float32x3 m_f3KeyboardDirection; // Direction vector of keyboard input
		Float32x2 m_f2PointerDelta; // Direction vector of keyboard input
		Float32 m_fPointerSmoothFrames; // Number of frames to smooth pointer data over 
		Point m_l2LastPointerPosition; // Last absolute position of mouse cursor
		Bool m_bPointerLeftButtonPressed; // Indicates whether the left mouse button is pressed
		Bool m_bPointerMiddleButtonPressed; // Indicates whether the middle mouse button is pressed
		Bool m_bPointerRightButtonPressed; // Indicates whether the right mouse button is pressed
		Int32 m_iPointerWheelDelta; // Middle wheel scroll amount
		Int32 m_iButtonMask; // Indicates which buttons are down

		/// Those members define camera behaviour

		Float32x4x4 m_f4x4View;					// View matrix.
		Float32x4x4 m_f4x4Proj; 				// Projection matrix.
		Float32x3 m_f3DefaultLookAt; 			// Default camera target position.
		Float32x3 m_f3DefaultEye; 				// Default camera position.
		Float32x3 m_f3LookAt; 					// Camera target position.
		Float32x3 m_f3Eye; 						// Camera position.
		Float32 m_fYaw; 						// Camera heading angle.
		Float32 m_fPitch; 						// Camera pitch angle.
		Float32x3 m_f3Velocity;					// Camera velocity.
		Float32x3 m_f3DragVelocity;				// Camera velocity when dragging.
		Float32x2 m_f2RotationVelocity;			// Camera rotation velocity.
		Float32 m_fTotalDragTimeToZero;			// Time it takes for velocity to go to 0.
		Float32 m_fDragTimer;					// Countdown timer to start dragging.
		Float32 m_fFieldOfView;					// Camera field of view.
		Float32 m_fAspectRatio;					// Camera width/height ratio.
		Float32 m_fNearPlane;					// Camera frustum near plane.
		Float32 m_fFarPlane;					// Camera frustum far plane.
		Float32 m_fRotationFactor;				// Camera scaler for rotation.
		Float32 m_fMovementFactor;				// Camera scaler for movement.
		Float32x3 m_f3MinBoundary;				// Min clip boundary point.
		Float32x3 m_f3MaxBoundary;				// Max clip boundary point.
		Rect m_l4Drag;							// Camera dragging rect.

		Bool m_bResetPointerAfterMovement;		// Resets the cursor position (the cursor will always have space to move).
		Bool m_bEnableVerticalMovement;			// Allowes the camera to move along the y axis.
		Bool m_bAllowPointerCapturing;			// Indicates whether the pointer capturing is allowed.
		Bool m_bClipToBoundary;					// Clips the cam to the boundary.
		Bool m_bEnableMovement;					// Allows the camera to move along the horizontal plane.
		Bool m_bMovementDrag;					// Indicates camera movement.
		Bool m_bInvertPitch;					// Inverts pitch axis

		HWND m_hWindow;							// Camera window.
		KeyMap m_KeyMap;

	protected:

		inline Bool WasKeyPressed( _In_ Byte i8Key ) { return IsFlagChecked( i8Key, _To_byte ButtonState::WasPressed );	}
		inline Bool IsKeyPressed( _In_ Byte i8Key ) { return IsFlagChecked( i8Key, _To_byte ButtonState::IsPressed );	}
		Byte _Basic_camera_map_key( _In_ UInt32 uKey );

		Void _Basic_camera_input( _In_ Bool bKeyboard, _In_ Bool bPointer );
		Void _Basic_camera_constraine( _In_ Float32x3Ptr pf3Point );
		Void _Basic_camera_velocity( _In_ Float32 fElapsed );
		Void _Basic_camera_pointer(); 

#pragma endregion Members

	public: // Properties

		BasicCamera();

		Void _Basic_camera_reset( 
			);

		Void _Basic_camera_view( 
			_In_ Float32x3ConstPtr pf3CameraEye, 
			_In_ Float32x3ConstPtr pf3CameraTarget 
			); 

		Void _Basic_camera_proj( 
			_In_ Float32 fFieldOfView, _In_ Float32 fAspectRatio, 
			_In_ Float32 fNearPlane, _In_ Float32 fFarPlane 
			);

		Void _Basic_camera_post_on_pointer_pressed();
		Void _Basic_camera_post_on_pointer_released();
		Void _Basic_camera_on_key_pressed( _In_ UInt32 uKey );
		Void _Basic_camera_on_key_released( _In_ UInt32 uKey );
		
		Void _Basic_camera_on_pointer_capture_changed();
		Void _Basic_camera_on_pointer_wheel_changed( _In_ Int16 iWheelDelta );

		Void _Basic_camera_on_pointer_left_released();
		Void _Basic_camera_on_pointer_right_released();
		Void _Basic_camera_on_pointer_middle_released();
		Void _Basic_camera_on_pointer_left_pressed( _In_ Int16 uX, _In_ Int16 uY );
		Void _Basic_camera_on_pointer_right_pressed( _In_ Int16 uX, _In_ Int16 uY );
		Void _Basic_camera_on_pointer_middle_pressed( _In_ Int16 uX, _In_ Int16 uY );

	};
};






